"""
AIQuant测试配置文件

提供测试所需的fixtures和配置
"""

import pytest
import asyncio
import os
import tempfile
from typing import AsyncGenerator, Generator
from unittest.mock import Mock, AsyncMock, patch

# 设置测试环境变量
os.environ["ENVIRONMENT"] = "testing"
os.environ["DATABASE_URL"] = "sqlite:///test.db"
os.environ["REDIS_URL"] = "redis://localhost:6379/15"  # 使用测试数据库
os.environ["SECRET_KEY"] = "test-secret-key-for-testing-only"
os.environ["JWT_SECRET_KEY"] = "test-jwt-secret-key-for-testing"


@pytest.fixture(scope="session")
def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]:
    """创建事件循环用于异步测试"""
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    yield loop
    loop.close()


@pytest.fixture(scope="session")
async def test_app():
    """创建测试应用实例"""
    try:
        from aiquant.main import app
        yield app
    except ImportError:
        # 如果无法导入，创建模拟应用
        mock_app = Mock()
        mock_app.state = Mock()
        yield mock_app


@pytest.fixture
async def test_database():
    """测试数据库连接"""
    try:
        from aiquant.core.database import get_database
        db = await get_database()
        
        # 创建测试表（如果需要）
        await _create_test_tables(db)
        
        yield db
        
        # 清理测试数据
        await _cleanup_test_data(db)
        
    except ImportError:
        # 创建模拟数据库
        mock_db = AsyncMock()
        mock_db.fetch_all = AsyncMock(return_value=[])
        mock_db.fetch_one = AsyncMock(return_value=None)
        mock_db.execute = AsyncMock()
        mock_db.commit = AsyncMock()
        mock_db.transaction = AsyncMock()
        yield mock_db


@pytest.fixture
async def test_redis():
    """测试Redis连接"""
    try:
        from aiquant.core.redis_client import get_redis_client
        redis = await get_redis_client()
        
        # 清理测试数据
        await redis.flushdb()
        
        yield redis
        
        # 清理测试数据
        await redis.flushdb()
        
    except ImportError:
        # 创建模拟Redis
        mock_redis = AsyncMock()
        mock_redis.get = AsyncMock(return_value=None)
        mock_redis.set = AsyncMock()
        mock_redis.delete = AsyncMock()
        mock_redis.hget = AsyncMock(return_value=None)
        mock_redis.hset = AsyncMock()
        mock_redis.hgetall = AsyncMock(return_value={})
        mock_redis.lpush = AsyncMock()
        mock_redis.lrange = AsyncMock(return_value=[])
        mock_redis.llen = AsyncMock(return_value=0)
        mock_redis.publish = AsyncMock()
        mock_redis.flushdb = AsyncMock()
        yield mock_redis


@pytest.fixture
def test_settings():
    """测试配置"""
    try:
        from aiquant.core.config import get_settings
        settings = get_settings()
        # 覆盖一些测试特定的设置
        settings.database_url = "sqlite:///test.db"
        settings.redis_url = "redis://localhost:6379/15"
        settings.environment = "testing"
        settings.debug = True
        yield settings
    except ImportError:
        # 创建模拟设置
        mock_settings = Mock()
        mock_settings.database_url = "sqlite:///test.db"
        mock_settings.redis_url = "redis://localhost:6379/15"
        mock_settings.environment = "testing"
        mock_settings.debug = True
        mock_settings.secret_key = "test-secret"
        mock_settings.jwt_secret_key = "test-jwt-secret"
        yield mock_settings


@pytest.fixture
async def test_user(test_database):
    """创建测试用户"""
    user_data = {
        "id": "test-user-123",
        "username": "testuser",
        "email": "test@example.com",
        "full_name": "Test User",
        "is_active": True,
        "created_at": "2024-01-01T00:00:00Z"
    }
    
    try:
        # 插入测试用户
        await test_database.execute("""
            INSERT INTO users (id, username, email, full_name, is_active, created_at)
            VALUES (:id, :username, :email, :full_name, :is_active, :created_at)
            ON CONFLICT (id) DO NOTHING
        """, user_data)
        await test_database.commit()
    except Exception:
        # 如果数据库操作失败，返回模拟数据
        pass
    
    yield user_data
    
    try:
        # 清理测试用户
        await test_database.execute("DELETE FROM users WHERE id = :id", {"id": user_data["id"]})
        await test_database.commit()
    except Exception:
        pass


@pytest.fixture
async def test_strategy(test_database, test_user):
    """创建测试策略"""
    strategy_data = {
        "id": "test-strategy-123",
        "name": "Test Strategy",
        "description": "A test strategy",
        "type": "momentum",
        "user_id": test_user["id"],
        "parameters": {
            "lookback_period": 20,
            "threshold": 0.02,
            "max_position_size": 10000
        },
        "is_active": False,
        "created_at": "2024-01-01T00:00:00Z"
    }
    
    try:
        await test_database.execute("""
            INSERT INTO strategies (id, name, description, type, user_id, parameters, is_active, created_at)
            VALUES (:id, :name, :description, :type, :user_id, :parameters, :is_active, :created_at)
            ON CONFLICT (id) DO NOTHING
        """, {
            **strategy_data,
            "parameters": str(strategy_data["parameters"])  # 转换为字符串存储
        })
        await test_database.commit()
    except Exception:
        pass
    
    yield strategy_data
    
    try:
        await test_database.execute("DELETE FROM strategies WHERE id = :id", {"id": strategy_data["id"]})
        await test_database.commit()
    except Exception:
        pass


@pytest.fixture
def test_market_data():
    """测试市场数据"""
    return [
        {
            "symbol": "AAPL",
            "timestamp": "2024-01-01T09:30:00Z",
            "open": 150.0,
            "high": 152.0,
            "low": 149.0,
            "close": 151.0,
            "volume": 1000000
        },
        {
            "symbol": "AAPL",
            "timestamp": "2024-01-01T09:31:00Z",
            "open": 151.0,
            "high": 153.0,
            "low": 150.0,
            "close": 152.0,
            "volume": 1100000
        },
        {
            "symbol": "GOOGL",
            "timestamp": "2024-01-01T09:30:00Z",
            "open": 2800.0,
            "high": 2820.0,
            "low": 2790.0,
            "close": 2810.0,
            "volume": 500000
        }
    ]


@pytest.fixture
def mock_coordination_engine():
    """模拟协调引擎"""
    mock_engine = AsyncMock()
    mock_engine.is_initialized = True
    mock_engine.agents = [
        {
            "id": "agent-1",
            "type": "data_collector",
            "status": "active",
            "capabilities": ["market_data", "news_analysis"]
        },
        {
            "id": "agent-2",
            "type": "strategy_executor",
            "status": "active",
            "capabilities": ["trade_execution", "risk_management"]
        }
    ]
    mock_engine.coordination_mode = "hierarchical"
    
    mock_engine.initialize = AsyncMock()
    mock_engine.register_agent = AsyncMock()
    mock_engine.unregister_agent = AsyncMock()
    mock_engine.set_coordination_mode = AsyncMock()
    mock_engine.distribute_task = AsyncMock(return_value={
        "task_id": "test-task-123",
        "assigned_agents": ["agent-1"],
        "estimated_completion_time": 30
    })
    
    return mock_engine


@pytest.fixture
def mock_strategy_manager():
    """模拟策略管理器"""
    mock_manager = AsyncMock()
    
    mock_strategy = Mock()
    mock_strategy.id = "test-strategy-123"
    mock_strategy.name = "Test Strategy"
    mock_strategy.is_active = False
    mock_strategy.process_data = AsyncMock(return_value=[])
    
    mock_manager.create_strategy = AsyncMock(return_value=mock_strategy)
    mock_manager.start_strategy = AsyncMock()
    mock_manager.stop_strategy = AsyncMock()
    mock_manager.remove_strategy = AsyncMock()
    mock_manager.is_strategy_active = Mock(return_value=False)
    
    return mock_manager


@pytest.fixture
def mock_celery_app():
    """模拟Celery应用"""
    mock_app = Mock()
    
    # 模拟控制接口
    mock_inspect = Mock()
    mock_inspect.stats = Mock(return_value={"worker1": {"pool": {"max-concurrency": 4}}})
    mock_inspect.active = Mock(return_value={"worker1": []})
    
    mock_control = Mock()
    mock_control.inspect = Mock(return_value=mock_inspect)
    
    mock_app.control = mock_control
    mock_app.conf = Mock()
    mock_app.conf.beat_schedule = {
        "update_market_data": {"task": "update_market_data", "schedule": 60.0},
        "cleanup_old_data": {"task": "cleanup_old_data", "schedule": 3600.0},
        "generate_daily_reports": {"task": "generate_daily_reports", "schedule": 86400.0}
    }
    
    return mock_app


@pytest.fixture
def mock_data_adapter():
    """模拟数据适配器"""
    mock_adapter = AsyncMock()
    
    mock_adapter.get_realtime_data = AsyncMock(return_value={
        "symbol": "AAPL",
        "price": 150.0,
        "timestamp": "2024-01-01T09:30:00Z",
        "change": 1.0,
        "change_percent": 0.67
    })
    
    mock_adapter.get_historical_data = AsyncMock(return_value=[
        {
            "symbol": "AAPL",
            "timestamp": "2024-01-01T09:30:00Z",
            "open": 150.0,
            "high": 152.0,
            "low": 149.0,
            "close": 151.0,
            "volume": 1000000
        }
    ])
    
    mock_adapter.get_symbols = AsyncMock(return_value=["AAPL", "GOOGL", "MSFT", "AMZN"])
    
    return mock_adapter


@pytest.fixture
def mock_auth_service():
    """模拟认证服务"""
    mock_service = Mock()
    
    mock_service.create_user = AsyncMock(return_value={
        "id": "user-123",
        "username": "testuser",
        "email": "test@example.com",
        "created_at": "2024-01-01T00:00:00Z"
    })
    
    mock_service.authenticate_user = AsyncMock(return_value={
        "access_token": "test-access-token",
        "token_type": "bearer",
        "expires_in": 3600
    })
    
    mock_service.get_current_user = AsyncMock(return_value={
        "id": "user-123",
        "username": "testuser",
        "email": "test@example.com"
    })
    
    mock_service.verify_token = AsyncMock(return_value=True)
    
    return mock_service


@pytest.fixture
def temp_file():
    """创建临时文件"""
    with tempfile.NamedTemporaryFile(delete=False) as f:
        yield f.name
    
    # 清理临时文件
    try:
        os.unlink(f.name)
    except OSError:
        pass


@pytest.fixture
def temp_directory():
    """创建临时目录"""
    with tempfile.TemporaryDirectory() as temp_dir:
        yield temp_dir


@pytest.fixture(autouse=True)
def mock_external_apis():
    """自动模拟外部API调用"""
    with patch('aiquant.data.external_apis.fetch_market_data') as mock_fetch:
        mock_fetch.return_value = {
            "symbol": "AAPL",
            "price": 150.0,
            "timestamp": "2024-01-01T09:30:00Z"
        }
        yield mock_fetch


@pytest.fixture
def mock_websocket():
    """模拟WebSocket连接"""
    mock_ws = AsyncMock()
    mock_ws.send = AsyncMock()
    mock_ws.recv = AsyncMock(return_value='{"type": "test", "data": {}}')
    mock_ws.close = AsyncMock()
    
    return mock_ws


# 辅助函数
async def _create_test_tables(db):
    """创建测试表"""
    try:
        # 用户表
        await db.execute("""
            CREATE TABLE IF NOT EXISTS users (
                id TEXT PRIMARY KEY,
                username TEXT UNIQUE NOT NULL,
                email TEXT UNIQUE NOT NULL,
                full_name TEXT,
                is_active BOOLEAN DEFAULT TRUE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)
        
        # 策略表
        await db.execute("""
            CREATE TABLE IF NOT EXISTS strategies (
                id TEXT PRIMARY KEY,
                name TEXT NOT NULL,
                description TEXT,
                type TEXT NOT NULL,
                user_id TEXT NOT NULL,
                parameters TEXT,
                is_active BOOLEAN DEFAULT FALSE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (user_id) REFERENCES users (id)
            )
        """)
        
        # 市场数据表
        await db.execute("""
            CREATE TABLE IF NOT EXISTS market_data (
                symbol TEXT NOT NULL,
                timestamp TIMESTAMP NOT NULL,
                open REAL NOT NULL,
                high REAL NOT NULL,
                low REAL NOT NULL,
                close REAL NOT NULL,
                volume INTEGER NOT NULL,
                PRIMARY KEY (symbol, timestamp)
            )
        """)
        
        await db.commit()
    except Exception as e:
        print(f"创建测试表失败: {e}")


async def _cleanup_test_data(db):
    """清理测试数据"""
    try:
        # 清理测试数据
        await db.execute("DELETE FROM market_data WHERE symbol LIKE 'TEST%'")
        await db.execute("DELETE FROM strategies WHERE name LIKE 'Test%'")
        await db.execute("DELETE FROM users WHERE username LIKE 'test%'")
        await db.commit()
    except Exception as e:
        print(f"清理测试数据失败: {e}")


# 测试标记
def pytest_configure(config):
    """配置pytest标记"""
    config.addinivalue_line(
        "markers", "integration: 标记集成测试"
    )
    config.addinivalue_line(
        "markers", "unit: 标记单元测试"
    )
    config.addinivalue_line(
        "markers", "slow: 标记慢速测试"
    )
    config.addinivalue_line(
        "markers", "api: 标记API测试"
    )
    config.addinivalue_line(
        "markers", "database: 标记数据库测试"
    )
    config.addinivalue_line(
        "markers", "redis: 标记Redis测试"
    )
    config.addinivalue_line(
        "markers", "websocket: 标记WebSocket测试"
    )


# 测试收集钩子
def pytest_collection_modifyitems(config, items):
    """修改测试收集"""
    for item in items:
        # 为集成测试添加标记
        if "integration" in item.nodeid:
            item.add_marker(pytest.mark.integration)
        
        # 为API测试添加标记
        if "api" in item.nodeid:
            item.add_marker(pytest.mark.api)
        
        # 为慢速测试添加标记
        if any(keyword in item.name.lower() for keyword in ["performance", "load", "stress"]):
            item.add_marker(pytest.mark.slow)


# 测试报告钩子
def pytest_html_report_title(report):
    """自定义HTML报告标题"""
    report.title = "AIQuant 测试报告"


def pytest_html_results_summary(prefix, summary, postfix):
    """自定义HTML报告摘要"""
    prefix.extend([
        "<h2>AIQuant系统测试</h2>",
        "<p>这是AIQuant量化投资系统的自动化测试报告。</p>"
    ])


# 性能测试辅助函数
@pytest.fixture
def performance_monitor():
    """性能监控器"""
    import time
    import psutil
    
    class PerformanceMonitor:
        def __init__(self):
            self.start_time = None
            self.start_memory = None
            self.start_cpu = None
        
        def start(self):
            self.start_time = time.time()
            self.start_memory = psutil.virtual_memory().used
            self.start_cpu = psutil.cpu_percent()
        
        def stop(self):
            end_time = time.time()
            end_memory = psutil.virtual_memory().used
            end_cpu = psutil.cpu_percent()
            
            return {
                "duration": end_time - self.start_time,
                "memory_delta": end_memory - self.start_memory,
                "cpu_usage": (self.start_cpu + end_cpu) / 2
            }
    
    return PerformanceMonitor()


# 数据生成器
@pytest.fixture
def data_generator():
    """测试数据生成器"""
    import random
    from datetime import datetime, timedelta
    
    class DataGenerator:
        @staticmethod
        def generate_market_data(symbol="AAPL", count=100):
            """生成市场数据"""
            data = []
            base_price = 150.0
            base_time = datetime.now() - timedelta(days=count)
            
            for i in range(count):
                timestamp = base_time + timedelta(minutes=i)
                price_change = random.uniform(-2, 2)
                
                open_price = base_price + price_change
                high_price = open_price + random.uniform(0, 2)
                low_price = open_price - random.uniform(0, 2)
                close_price = open_price + random.uniform(-1, 1)
                volume = random.randint(500000, 2000000)
                
                data.append({
                    "symbol": symbol,
                    "timestamp": timestamp.isoformat(),
                    "open": round(open_price, 2),
                    "high": round(high_price, 2),
                    "low": round(low_price, 2),
                    "close": round(close_price, 2),
                    "volume": volume
                })
                
                base_price = close_price
            
            return data
        
        @staticmethod
        def generate_user_data(count=10):
            """生成用户数据"""
            users = []
            for i in range(count):
                users.append({
                    "id": f"user-{i+1}",
                    "username": f"testuser{i+1}",
                    "email": f"testuser{i+1}@example.com",
                    "full_name": f"Test User {i+1}",
                    "is_active": True,
                    "created_at": datetime.now().isoformat()
                })
            return users
    
    return DataGenerator()


# 清理钩子
@pytest.fixture(autouse=True)
def cleanup_after_test():
    """测试后自动清理"""
    yield
    
    # 这里可以添加测试后的清理逻辑
    # 例如清理临时文件、重置状态等
    pass